package com.atguigu.tingshu.user.service.impl;

import com.alibaba.nacos.common.utils.JacksonUtils;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.constant.TingShuMqConstants;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.service.RabbitmqService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.common.util.MongoUtil;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.TrackStatMqVo;
import com.atguigu.tingshu.vo.base.user.UserListenProcess;
import com.atguigu.tingshu.user.service.UserListenProcessService;
import com.atguigu.tingshu.vo.user.UserListenProcessListVo;
import com.atguigu.tingshu.vo.user.UserListenProcessVo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.rabbitmq.tools.json.JSONUtil;
import jakarta.annotation.Resource;
import nonapi.io.github.classgraph.json.JSONUtils;
import org.apache.tomcat.util.json.JSONParser;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.commons.util.IdUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserListenProcessServiceImpl implements UserListenProcessService {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RabbitmqService rabbitmqService;
    @Resource
    private AlbumInfoFeignClient albumInfoFeignClient;
    @Resource
    private TrackInfoFeignClient trackInfoFeignClient;

    @Override
    public Page<UserListenProcessListVo> findUserPage(Integer page, Integer limit) {
        Page<UserListenProcessListVo> userPage = new Page<>(page, limit);
        Long userId = AuthContextHolder.getUserId();
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);
        List<UserListenProcess> userListenProcesses = mongoTemplate.find(Query.query(Criteria.where("userId").is(userId)),
                UserListenProcess.class, collectionName);
        List<UserListenProcessListVo> userListenProcessListVos = new ArrayList<>();
        userListenProcesses.forEach(userListenProcess -> {
            Result<AlbumListVo> userAlbumR = albumInfoFeignClient.findUserAlbum(userListenProcess.getAlbumId());
            Assert.notNull(userAlbumR, "分页查询历史记录时，远程调用专辑信息失败！");
            AlbumListVo albumListVo = userAlbumR.getData();

            Result<TrackInfo> trackInfoResult = trackInfoFeignClient.getTrackInfo(userListenProcess.getTrackId());
            Assert.notNull(trackInfoResult, "分页查询历史记录时，远程调用声音信息失败！");
            TrackInfo trackInfo = trackInfoResult.getData();

            UserListenProcessListVo userListenProcessListVo = new UserListenProcessListVo();
            BeanUtils.copyProperties(userListenProcess, userListenProcessListVo);
            userListenProcessListVo.setAlbumTitle(albumListVo.getAlbumTitle());
            userListenProcessListVo.setTrackTitle(trackInfo.getTrackTitle());
            userListenProcessListVo.setCoverUrl(albumListVo.getCoverUrl());
            userListenProcessListVo.setMediaDuration(trackInfo.getMediaDuration());

            BigDecimal bigDecimal = BigDecimal.valueOf((userListenProcess.getBreakSecond().doubleValue() / trackInfo.getMediaDuration().doubleValue()) * 100);
            BigDecimal playRate = bigDecimal.setScale(2, RoundingMode.HALF_UP);
            userListenProcessListVo.setPlayRate(playRate + "%");

            userListenProcessListVos.add(userListenProcessListVo);
        });
        userPage.setRecords(userListenProcessListVos);
        return userPage;
    }

    @Override
    public BigDecimal getTrackBreakSecond(Long trackId) {
        Long userId = AuthContextHolder.getUserId();
        if (userId == null) return new BigDecimal(0);
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);
        UserListenProcess userListenProcess = mongoTemplate.findOne(Query.query(Criteria.where("userId").is(userId).and("trackId").is(trackId)), UserListenProcess.class, collectionName);
        if (userListenProcess == null) return new BigDecimal(0);
        return userListenProcess.getBreakSecond();
    }

    @Override
    public void updateListenProcess(UserListenProcessVo userListenProcessVo) {
        Long userId = AuthContextHolder.getUserId();
        if (userId == null) return;
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);
        Long trackId = userListenProcessVo.getTrackId();
        UserListenProcess userListenProcess = mongoTemplate.findOne(Query.query(Criteria.where("userId").is(userId)
                .and("trackId").is(trackId)), UserListenProcess.class, collectionName);
        if (userListenProcess == null) {
            userListenProcess = new UserListenProcess();
            BeanUtils.copyProperties(userListenProcessVo, userListenProcess);
            userListenProcess.setUserId(userId);
            userListenProcess.setIsShow(1);
            userListenProcess.setCreateTime(new Date());
            userListenProcess.setUpdateTime(userListenProcess.getCreateTime());
        } else {
            userListenProcess.setBreakSecond(userListenProcessVo.getBreakSecond());
            userListenProcess.setUpdateTime(new Date());
        }
        mongoTemplate.save(userListenProcess, collectionName);

        //判断四个一,同一个用户同一个声音一天只能统计一次
        String key = RedisConstant.USER_LISTEN_PROCESS_PREFIX + userId;
        //查询当前声音是够被统计过
        Boolean flag = redisTemplate.opsForValue().getBit(key, trackId);
        if (Boolean.TRUE.equals(flag)) return;
        //设置已统计的标记
        redisTemplate.opsForValue().setBit(key, trackId, true);
        LocalDateTime endTime = LocalDateTime.of(LocalDate.now().plusDays(1), LocalTime.MIN);
        long timeOut = ChronoUnit.MILLIS.between(LocalDateTime.now(), endTime);
        //设置标记的过期时间
        redisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        //封装要发送的消息
        TrackStatMqVo trackStatMqVo = new TrackStatMqVo();
        trackStatMqVo.setTrackId(trackId);
        trackStatMqVo.setAlbumId(userListenProcessVo.getAlbumId());
        trackStatMqVo.setCount(1);
        trackStatMqVo.setStatType(SystemConstant.TRACK_STAT_PLAY);
        trackStatMqVo.setBusinessNo(UUID.randomUUID().toString());
        //发送消息
        rabbitmqService.sendMqMsg(TingShuMqConstants.USER_EXCHANGE, TingShuMqConstants.TRACK_STAT_RK, JacksonUtils.toJson(trackStatMqVo));
    }

    @Override
    public Map<String, Long> getLatelyTrack() {
        Long userId = AuthContextHolder.getUserId();
        Map<String, Long> map = new HashMap<>();
        if (userId == null) {
            map.put("trackId", 51947L);
            map.put("albumId", 1595L);
            return map;
        }
        String collectionName = MongoUtil.getCollectionName(MongoUtil.MongoCollectionEnum.USER_LISTEN_PROCESS, userId);
        UserListenProcess userListenProcess = mongoTemplate.findOne(Query.query(Criteria.where("userId").is(userId)).with(Sort.by(Sort.Order.desc("updateTime"))).limit(1),
                UserListenProcess.class, collectionName);
        if (userListenProcess == null) {
            map.put("trackId", 51947L);
            map.put("albumId", 1595L);
            return map;
        }
        map.put("trackId", userListenProcess.getTrackId());
        map.put("albumId", userListenProcess.getAlbumId());
        return map;
    }
}
